﻿using System;
using System.Windows;
using System.Windows.Threading;
using System.Threading;
using System.Collections.Generic;

using SBPweb.PROvibe.Shared.Contracts.Content;

using SBPweb.PROvibe.Client.Framework.Contracts.Content;
using SBPweb.PROvibe.Client.Framework.Content.Connector;
using SBPweb.PROvibe.Client.Framework.Modularity;
using SBPweb.PROvibe.Client.Framework.Utilities.Events;

namespace SBPweb.PROvibe.Client.Framework.Content
{

    public class ContentManager : IContentManager
    {

        #region Private fields

        private IPage currentPage;
        private ClientDescriptor clientDescriptor;
        private ISessionManager sessionManager;
        private IThemeManager themeManager;
        private ConnectorClient connector;
        private Thread threadNormal;
        private Dispatcher dispatcher;

        #endregion

        #region Constructor logic

        public ContentManager()
        {
            this.dispatcher = Application.Current.RootVisual.Dispatcher;
            this.CurrentPageLoaded += new EventHandler<TypedObjectEventArgs<IPage>>(ContentManager_CurrentPageLoaded);
            this.ThemeManagerLoaded += new EventHandler<TypedObjectEventArgs<IThemeManager>>(ContentManager_ThemeManagerLoaded);
        }

        #endregion

        #region Property accessors

        public ClientDescriptor Client
        {
            get
            {
                return clientDescriptor;
            }
        }

        public ISessionManager Session
        {
            get
            {
                return sessionManager;
            }
        }

        public IThemeManager Themes
        {
            get
            {
                return themeManager;
            }
        }

        public IPage CurrentPage
        {
            get
            {
                return currentPage;
            }
        }

        #endregion

        #region Events

        public event EventHandler Initializing;

        public event EventHandler Initialized;

        public event EventHandler Loading;

        public event EventHandler Loaded;

        public event EventHandler<TypedObjectEventArgs<IPage>> CurrentPageLoaded;

        public event EventHandler<TypedObjectEventArgs<ISmartpart>> SmartpartLoaded;

        public event EventHandler<TypedObjectEventArgs<IThemeManager>> ThemeManagerLoaded;

        #endregion

        #region Methods

        public void Initialize(ClientDescriptor clientDescriptor)
        {
            if (Initializing != null)
            {
                Initializing(this, EventArgs.Empty);
            }
            threadNormal = Thread.CurrentThread;
            if (string.IsNullOrEmpty(threadNormal.Name))
            {
                threadNormal.Name = "ThreadNormal";
            }

            this.clientDescriptor = clientDescriptor;
            this.connector = new ConnectorClient();
            this.sessionManager = new SessionManager(this.clientDescriptor, connector, SessionPersistencyMode.DirectSynchronous);
            this.connector.GetTypeDescriptorByIdCompleted += new EventHandler<GetTypeDescriptorByIdCompletedEventArgs>(Connector_GetTypeDescriptorCompleted);
            this.connector.GetCurrentPageIdCompleted += new EventHandler<GetCurrentPageIdCompletedEventArgs>(Connector_GetCurrentPageIdCompleted);
            this.connector.GetCurrentThemeManagerIdCompleted += new EventHandler<GetCurrentThemeManagerIdCompletedEventArgs>(Connector_GetCurrentThemeManagerIdCompleted);

            if (Initialized != null)
            {
                Initialized(this, EventArgs.Empty);
            }
        }

        public void LoadCurrentPage()
        {
            OnLoading();
            connector.GetCurrentPageIdAsync(clientDescriptor);
        }

        public void LoadCurrentThemeManager()
        {
            OnLoading();
            connector.GetCurrentThemeManagerIdAsync(clientDescriptor);
        }

        #region Loading Smartparts

        public void LoadPage<TType>(TypeDescriptor pageDescriptor)
            where TType : IPage
        {
            dispatcher.BeginInvoke(delegate()
            {
                Modularity.ModuleLoader.CreateInstance<IPage>((TypeDescriptor)pageDescriptor, CurrentPageLoaded, this as IContentManager);
            });
        }

        public void LoadSmartpart<TType>(TypeDescriptor smartpartDescriptor, EventHandler<TypedObjectEventArgs<TType>> loadedHandler, params object[] constructorParameters)
            where TType : ISmartpart
        {
            dispatcher.BeginInvoke(delegate()
            {
                Modularity.ModuleLoader.CreateInstance<TType>((TypeDescriptor)smartpartDescriptor, loadedHandler, constructorParameters);
            });
        }

        public void LoadSmartpart<TType>(int moduleId, EventHandler<TypedObjectEventArgs<TType>> loadedHandler, params object[] constructorParameters)
            where TType : ISmartpart
        {
            LoadSmartpart<TType>(moduleId, loadedHandler, CreateSmartpartInstance<TType>, constructorParameters);
        }

        internal void LoadSmartpart<TType>(int moduleId, EventHandler<TypedObjectEventArgs<TType>> loadedHandler, EventHandler<LoadSmartpartParameterEventArgs<TType>> loadingHandler, params object[] constructorParameters)
            where TType : ISmartpart
        {
            LoadSmartpartParameterEventArgs<TType> lspe = new LoadSmartpartParameterEventArgs<TType>(loadedHandler, loadingHandler, constructorParameters);
            connector.GetTypeDescriptorByIdAsync(moduleId, lspe);
        }

        #endregion

        #region Loading ThemeManagers

        public void LoadThemeManager<TType>(TypeDescriptor themeManagerDescriptor)
            where TType : IThemeManager
        {
            dispatcher.BeginInvoke(delegate()
            {
                Modularity.ModuleLoader.CreateInstance<IThemeManager>((TypeDescriptor)themeManagerDescriptor, ThemeManagerLoaded, this as IContentManager);
            });
        }

        internal void LoadThemeManager<TType>(int themeManagerId, EventHandler<TypedObjectEventArgs<TType>> loadedHandler, EventHandler<LoadThemeManagerParameterEventArgs<TType>> loadingHandler, params object[] constructorParameters)
            where TType : IThemeManager
        {
            LoadThemeManagerParameterEventArgs<TType> lspe = new LoadThemeManagerParameterEventArgs<TType>(loadedHandler, loadingHandler, constructorParameters);
            connector.GetTypeDescriptorByIdAsync(themeManagerId, lspe);
        }

        #endregion

        #region Loading CustomElements

        public void LoadElement<TType>(TypeDescriptor elementDescriptor, EventHandler<TypedObjectEventArgs<TType>> loadedHandler, params object[] constructorParameters)
            where TType : UIElement
        {
            dispatcher.BeginInvoke(delegate()
            {
                Modularity.ModuleLoader.CreateInstance<TType>((TypeDescriptor)elementDescriptor, loadedHandler, constructorParameters);
            });
        }

        #endregion

        #region Async callbacks

        private void CreateSmartpartInstance<TType>(object sender, LoadSmartpartParameterEventArgs<TType> e)
            where TType : ISmartpart
        {
            dispatcher.BeginInvoke(delegate()
            {
                Modularity.ModuleLoader.CreateInstance<TType>(e.Type, e.Loaded, e.Parameters);
            });
        }

        private void CreateThemeManagerInstance<TType>(object sender, LoadThemeManagerParameterEventArgs<TType> e)
            where TType : IThemeManager
        {
            dispatcher.BeginInvoke(delegate()
            {
                Modularity.ModuleLoader.CreateInstance<TType>(e.Type, e.Loaded, e.Parameters);
            });
        }

        private void Connector_GetTypeDescriptorCompleted(object sender, GetTypeDescriptorByIdCompletedEventArgs e)
        {
            LoadParameterEventArgs lspe = e.UserState as LoadParameterEventArgs;
            lspe.Type = (TypeDescriptor)e.Result;
            lspe.Loading.DynamicInvoke(sender, lspe);
        }

        private void Connector_GetCurrentPageIdCompleted(object sender, GetCurrentPageIdCompletedEventArgs e)
        {
            this.LoadSmartpart<IPage>(e.Result, CurrentPageLoaded, CreateSmartpartInstance<IPage>, this as IContentManager);
        }

        private void Connector_GetCurrentThemeManagerIdCompleted(object sender, GetCurrentThemeManagerIdCompletedEventArgs e)
        {
            this.LoadThemeManager<IThemeManager>(e.Result, ThemeManagerLoaded, CreateThemeManagerInstance<IThemeManager>, this as IContentManager);
        }

        private void ContentManager_CurrentPageLoaded(object sender, TypedObjectEventArgs<IPage> e)
        {
            currentPage = e.Object;
            OnLoaded();
        }

        private void ContentManager_ThemeManagerLoaded(object sender, TypedObjectEventArgs<IThemeManager> e)
        {
            themeManager = e.Object;
            OnLoaded();
        }

        #endregion

        #region Other stuff

        protected virtual void OnLoading()
        {
            dispatcher.BeginInvoke(delegate()
            {
                if (Loading != null)
                {
                    Loading(this, EventArgs.Empty);
                }
            });
        }

        protected virtual void OnLoaded()
        {
            dispatcher.BeginInvoke(delegate()
            {
                if (Loaded != null)
                {
                    Loaded(this, EventArgs.Empty);
                }
            });
        }

        #endregion

        #endregion

        #region Subclasses

        internal class LoadParameterEventArgs : EventArgs
        {
            internal LoadParameterEventArgs(MulticastDelegate loadedHandler, MulticastDelegate loadingHandler, params object[] parameters)
            {
                Loaded = loadedHandler;
                Loading = loadingHandler;
                Parameters = parameters;
            }

            internal TypeDescriptor Type;

            internal MulticastDelegate Loading;

            internal MulticastDelegate Loaded;

            internal object[] Parameters;

        }

        #region Smartpart

        internal class LoadSmartpartParameterEventArgs<TType> : LoadParameterEventArgs
            where TType : ISmartpart
        {

            internal LoadSmartpartParameterEventArgs(EventHandler<TypedObjectEventArgs<TType>> loadedHandler, EventHandler<LoadSmartpartParameterEventArgs<TType>> loadingHandler, params object[] parameters)
                : base(loadedHandler, loadingHandler, parameters)
            {
            }

            internal new EventHandler<LoadSmartpartParameterEventArgs<TType>> Loading
            {
                get
                {
                    return (EventHandler<LoadSmartpartParameterEventArgs<TType>>)base.Loading;
                }
                set
                {
                    base.Loading = value;
                }
            }

            internal new EventHandler<TypedObjectEventArgs<TType>> Loaded
            {
                get
                {
                    return (EventHandler<TypedObjectEventArgs<TType>>)base.Loaded;
                }
                set
                {
                    base.Loaded = value;
                }
            }

        }

        #endregion

        #region ThemeManager

        internal class LoadThemeManagerParameterEventArgs<TType> : LoadParameterEventArgs
            where TType : IThemeManager
        {

            internal LoadThemeManagerParameterEventArgs(EventHandler<TypedObjectEventArgs<TType>> loadedHandler, EventHandler<LoadThemeManagerParameterEventArgs<TType>> loadingHandler, params object[] parameters)
                : base(loadedHandler, loadingHandler, parameters)
            {
            }

            internal new EventHandler<LoadThemeManagerParameterEventArgs<TType>> Loading
            {
                get
                {
                    return (EventHandler<LoadThemeManagerParameterEventArgs<TType>>)base.Loading;
                }
                set
                {
                    base.Loading = value;
                }
            }

            internal new EventHandler<TypedObjectEventArgs<TType>> Loaded
            {
                get
                {
                    return (EventHandler<TypedObjectEventArgs<TType>>)base.Loaded;
                }
                set
                {
                    base.Loaded = value;
                }
            }

        }

        #endregion

        #endregion

    }

}